﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL4;
using OpenTK;

namespace Painter3D.RenderSys
{
    /// <summary>
    /// 全局变量块抽象，负责建立与赋值维护
    /// </summary>
    class UniformBlock : IDisposable
    {
        /// <summary>
        /// 全局变量被存储在该链表
        /// </summary>
        static private List<UniformBlock> blocks = new List<UniformBlock>();

        static public int GetBlockBinding(ShaderInfo.UniformBlockInfo blockInfo)
        {
            //从已经注册的变量块中查找
            foreach (var blockU in blocks)
            {
                if (blockU.blockInfo.blockName == blockInfo.blockName)
                {
                    for (int i = 0; i < blockU.blockInfo.uniformInfos.Length; i++)
                    {
                        if (!blockU.blockInfo.uniformInfos[i].Equals(blockInfo.uniformInfos[i]))
                        {
                            throw new Exception("同名变量块但却有着不同结构定义");
                        }
                    }
                    return blockU.blockInfo.bindingPoint;
                }
            }
            //创建新的变量块
            blocks.Add(new UniformBlock(blockInfo));
            return blocks[blocks.Count - 1].blockInfo.bindingPoint;
        }

        static public bool SetValue<T>(string block, string uniform, T value) where T : struct
        {
            foreach (var blockU in blocks)
            {
                if (blockU.blockInfo.blockName == block)
                {
                    return blockU.SetValue(uniform, value);
                }
            }
            return false;
        }

        static public bool GetValue<T>(string block, string uniform, out T value) where T : struct
        {
            foreach (var blockU in blocks)
            {
                if (blockU.blockInfo.blockName == block)
                {
                    return blockU.GetValue(uniform, out value);
                }
            }
            value = default(T);
            return false;
        }

        static public void Clear()
        {
            if (blocks.Count != 0)
            {
                blocks[0].Dispose();
            }
        }


        public readonly ShaderInfo.UniformBlockInfo blockInfo;
        private int UBO;
        private object[] data;


        private UniformBlock(ShaderInfo.UniformBlockInfo blockInfo)
        {
            this.blockInfo = blockInfo;
            this.blockInfo.bindingPoint = blocks.Count;
            data = new object[blockInfo.uniformInfos.Length];
            UBO = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.UniformBuffer, UBO);
            GL.BufferData(BufferTarget.UniformBuffer, blockInfo.byteSizeInSTD140, (IntPtr)null, BufferUsageHint.DynamicDraw);
            GL.BindBuffer(BufferTarget.UniformBuffer, 0);
            GL.BindBufferRange(BufferRangeTarget.UniformBuffer, this.blockInfo.bindingPoint, UBO, (IntPtr)0, blockInfo.byteSizeInSTD140);
        }

        /// <summary>
        /// 设置参数值，自动送入显存
        /// </summary>
        /// <typeparam name="T">参数类型</typeparam>
        /// <param name="name">参数变量名</param>
        /// <param name="value">取值</param>
        /// <returns>是否成功</returns>
        private bool SetValue<T>(string name, T value) where T : struct
        {
            for (int i = 0; i < blockInfo.uniformInfos.Length; i++)
            {
                if (blockInfo.uniformInfos[i].name == name)
                {
                    ShaderInfo.UniformInfo uinfo = blockInfo.uniformInfos[i];
                    if (uinfo.type != typeof(T))
                    {
                        return false;
                    }
                    data[i] = value;
                    //向显存发送数据
                    GL.BindBuffer(BufferTarget.UniformBuffer, UBO);
                    GL.BufferSubData(BufferTarget.UniformBuffer, (IntPtr)uinfo.index, Marshal.SizeOf(uinfo.type), ref value);
                    GL.BindBuffer(BufferTarget.UniformBuffer, 0);
                    return true;
                }
            }
            return false;
        }

        private bool GetValue<T>(string name, out T value) where T : struct
        {
            for (int i = 0; i < blockInfo.uniformInfos.Length; i++)
            {
                if (blockInfo.uniformInfos[i].name == name)
                {
                    if (blockInfo.uniformInfos[i].type != typeof(T))
                    {
                        value = default(T);
                        return false;
                    }
                    value = (T)data[i];
                    return true;
                }
            }
            value = default(T);
            return false;
        }
        /// <summary>
        /// 将所有数据送入显存
        /// </summary>
        public void UploadData()
        {
            GL.BindBuffer(BufferTarget.UniformBuffer, UBO);
            for (int i = 0; i < blockInfo.uniformInfos.Length; i++)
            {
                ShaderInfo.UniformInfo uinfo = blockInfo.uniformInfos[i];
                GL.BufferSubData(BufferTarget.UniformBuffer, (IntPtr)uinfo.index, Marshal.SizeOf(uinfo.type), (IntPtr)data[i]);
            }
            GL.BindBuffer(BufferTarget.UniformBuffer, 0);
        }

        public void Dispose()
        {
            blocks.Remove(this);
            GL.DeleteBuffer(UBO);
        }
    }
}
